import torch
import numpy as np

# 直接生成张量
data = [[1, 2], [3, 4]]
x_data = torch.tensor(data)
print("x_data = ", x_data)
print("x_data.shape = ", x_data.shape)

# 通过Numpy数组来生成张量
# 张量和Numpy array数组在CPU上可以共用一块内存区域, 改变其中一个另一个也会随之改变。
np_array = np.array(data)
x_np = torch.from_numpy(np_array)
print("x_np = ", x_np)

# tensor 转换为 numpy
a=torch.tensor([1,2,3])
print(type(a))
print(a.dtype)
b=a.numpy()
print(type(b))
print(b.dtype)

# 注，在计算图中将tensor转换为numpy，会导致计算图中断，因为numpy不支持自动求导
# a=torch.tensor([1,2,3], requires_grad=True)  # 生成一个tensor, 并设置需要求导
# 待转换类型的PyTorch Tensor变量带有梯度，直接将其转换为numpy数据将破坏计算图，因此numpy拒绝进行数据转换，
# 实际上这是对开发者的一种提醒。如果自己在转换数据时不需要保留梯度信息，可以在变量转换之前添加detach()调用。
# y.numpy() ---> y.detach().numpy() 或 y.data.numpy()

# 通过其他张量来生成张量
x_ones = torch.ones_like(x_data)  # 保持x_data的形状
print("x_ones = ", x_ones)
x_rand = torch.rand_like(x_data, dtype=torch.float)  # 重写数据类型
print("x_rand = ", x_rand)

# 通过指定数据维度来生成张量
shape = (2, 3,)
rand_tensor = torch.rand(shape)
ones_tensor = torch.ones(shape)
zeros_tensor = torch.zeros(shape)
print(f"Random Tensor: \n {rand_tensor} \n")
print(f"Ones Tensor: \n {ones_tensor} \n")
print(f"Zeros Tensor: \n {zeros_tensor}")

# 张量属性
tensor = torch.rand(3, 4)
print(f"Shape of tensor: {tensor.shape}")
print(f"Datatype of tensor: {tensor.dtype}")
print(f"Device tensor is stored on: {tensor.device}")

# 判断当前环境GPU是否可用, 然后将tensor导入GPU内运行
if torch.cuda.is_available():
    tensor = tensor.to('cuda')

# 张量的索引和切片
tensor = torch.ones(4, 4)
tensor[:, 1] = 0  # 将第1列(从0开始)的数据全部赋值为0
print("tensor = ", tensor)

# 张量的拼接
t1 = torch.cat([tensor, tensor, tensor], dim=1)
print("t1 = ", t1)

# 逐个元素相乘结果
print(f"tensor.mul(tensor): \n {tensor.mul(tensor)} \n")
# 等价写法:
print(f"tensor * tensor: \n {tensor * tensor}")

# 矩阵乘法
print(f"tensor.matmul(tensor.T): \n {tensor.matmul(tensor.T)} \n")
# 等价写法:
print(f"tensor @ tensor.T: \n {tensor @ tensor.T}")

# 维度压缩/解压
x = torch.linspace(-1, 1, 100)  # x是一个一维张量，包含100个元素，从-1到1均匀分布
print("\nx.shape = ", x.shape)
x = torch.unsqueeze(x, dim=1)  # 在维度1加了一维，x是一个二维张量，包含100行1列
print("x.shape = ", x.shape)
x = torch.squeeze(x)  # 压缩维度1，x是一个一维张量，包含100个元素
print("x.shape = ", x.shape)
